package com.wcl.playground.transaction.afterCommitExecutor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class AfterCommitExecutorImpl extends TransactionSynchronizationAdapter implements AfterCommitExecutor {
    private static final Logger LOGGER = LoggerFactory.getLogger(AfterCommitExecutorImpl.class);
    private static final ThreadLocal<List<Runnable>> RUNNABLES = new ThreadLocal();
    private ExecutorService threadPool;
    @Value("${afterCommit.thread.count:4}")
    private int threadCount;

    @Value("${afterCommit.securityEnable:true}")
    private boolean securityEnable;

    public AfterCommitExecutorImpl() {
    }

    public void execute(Runnable runnable) {
        if (!Boolean.FALSE.equals(this.securityEnable)) {
            LOGGER.info("Submitting new runnable {} to run after commit", runnable);
            if (!TransactionSynchronizationManager.isSynchronizationActive()) {
                LOGGER.info("Transaction synchronization is NOT ACTIVE. Executing right now runnable {}", runnable);
                runnable.run();
            } else {
                List<Runnable> threadRunnables = RUNNABLES.get();
                if (threadRunnables == null) {
                    threadRunnables = new ArrayList();
                    RUNNABLES.set(threadRunnables);
                    TransactionSynchronizationManager.registerSynchronization(this);
                }

                threadRunnables.add(runnable);
            }
        }
    }

    public void afterCommit() {
        List<Runnable> threadRunnables = RUNNABLES.get();
        Iterator iterator = threadRunnables.iterator();
        while(iterator.hasNext()) {
            Runnable runnable = (Runnable)iterator.next();
            try {
                this.threadPool.execute(runnable);
            } catch (RuntimeException var5) {
                LOGGER.error("Failed to execute runnable " + runnable, var5);
            }
        }

    }

    public void afterCompletion(int status) {
        LOGGER.info("Transaction completed with status {}", status == 0 ? "COMMITTED" : "ROLLED_BACK");
        RUNNABLES.get().clear();
        RUNNABLES.remove();
    }

    @PostConstruct
    public void init() {
        this.threadPool = Executors.newFixedThreadPool(this.threadCount > 0 ? this.threadCount : Runtime.getRuntime().availableProcessors() * 2);
    }

    @PreDestroy
    public void shutDown() {
        this.threadPool.shutdown();
        LOGGER.info("shutdown threadPool");
    }
}
